<!DOCTYPE html>
<!--
Copyright (C) 2017 The Android Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-project-detail-list</title>

<script src="../../../bower_components/page/page.js"></script>
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-project-detail-list.html">

<script>void(0);</script>

<test-fixture id="basic">
  <template>
    <gr-project-detail-list></gr-project-detail-list>
  </template>
</test-fixture>

<script>
  let counter;
  const branchGenerator = () => {
    return {
      ref: `refs/heads/test${++counter}`,
      revision: '9c9d08a438e55e52f33b608415e6dddd9b18550d',
      web_links: [
        {
          name: 'diffusion',
          url: `https://git.example.org/branch/test;refs/heads/test${counter}`,
        },
      ],
    };
  };
  const tagGenerator = () => {
    return {
      ref: `refs/tags/test${++counter}`,
      revision: '9c9d08a438e55e52f33b608415e6dddd9b18550d',
      web_links: [
        {
          name: 'diffusion',
          url: `https://git.example.org/tag/test;refs/tags/test${counter}`,
        },
      ],
      message: 'Annotated tag',
      tagger: {
        name: 'Test User',
        email: 'test.user@gmail.com',
        date: '2017-09-19 14:54:00.000000000',
        tz: 540,
      },
    };
  };

  suite('Branches', () => {
    let element;
    let branches;
    let sandbox;

    setup(() => {
      sandbox = sinon.sandbox.create();
      element = fixture('basic');
      element.detailType = 'branches';
      counter = 0;
      sandbox.stub(page, 'show');
    });

    teardown(() => {
      sandbox.restore();
    });

    suite('list of project branches', () => {
      setup(done => {
        branches = [{
          ref: 'HEAD',
          revision: 'master',
        }].concat(_.times(25, branchGenerator));

        stub('gr-rest-api-interface', {
          getProjectBranches(num, project, offset) {
            return Promise.resolve(branches);
          },
        });

        const params = {
          project: 'test',
          detailType: 'branches',
        };

        element._paramsChanged(params).then(() => { flush(done); });
      });

      test('test for branch in the list', done => {
        flush(() => {
          assert.equal(element._items[2].ref, 'refs/heads/test2');
          done();
        });
      });

      test('test for web links in the branches list', done => {
        flush(() => {
          assert.equal(element._items[2].web_links[0].url,
              'https://git.example.org/branch/test;refs/heads/test2');
          done();
        });
      });

      test('test for refs/heads/ being striped from ref', done => {
        flush(() => {
          assert.equal(element._stripRefs(element._items[2].ref,
              element.detailType), 'test2');
          done();
        });
      });

      test('_shownItems', () => {
        assert.equal(element._shownItems.length, 25);
      });

      test('Edit HEAD button not admin', done => {
        sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
        sandbox.stub(element.$.restAPI, 'getProjectAccess').returns(
            Promise.resolve({
              test: {is_owner: false},
            }));
        element._determineIfOwner('test').then(() => {
          assert.equal(element._isOwner, false);
          assert.equal(getComputedStyle(Polymer.dom(element.root)
              .querySelector('.revisionNoEditing')).display, 'inline');
          assert.equal(getComputedStyle(Polymer.dom(element.root)
              .querySelector('.revisionEdit')).display, 'none');
          done();
        });
      });

      test('Edit HEAD button admin', done => {
        const saveBtn = Polymer.dom(element.root).querySelector('.saveBtn');
        const cancelBtn = Polymer.dom(element.root).querySelector('.cancelBtn');
        const editBtn = Polymer.dom(element.root).querySelector('.editBtn');
        const revisionNoEditing = Polymer.dom(element.root)
              .querySelector('.revisionNoEditing');
        const revisionWithEditing = Polymer.dom(element.root)
              .querySelector('.revisionWithEditing');

        sandbox.stub(element, '_getLoggedIn').returns(Promise.resolve(true));
        sandbox.stub(element.$.restAPI, 'getProjectAccess').returns(
            Promise.resolve({
              test: {is_owner: true},
            }));
        sandbox.stub(element, '_handleSaveRevision');
        element._determineIfOwner('test').then(() => {
          assert.equal(element._isOwner, true);
          // The revision container for non-editing enabled row is not visible.
          assert.equal(getComputedStyle(revisionNoEditing).display, 'none');

          // The revision container for editing enabled row is visible.
          assert.notEqual(getComputedStyle(Polymer.dom(element.root)
              .querySelector('.revisionEdit')).display, 'none');

          // The revision and edit button are visible.
          assert.notEqual(getComputedStyle(revisionWithEditing).display,
              'none');
          assert.notEqual(getComputedStyle(editBtn).display, 'none');

          // The input, cancel, and save buttons are not visible.
          const hiddenElements = Polymer.dom(element.root)
              .querySelectorAll('.canEdit .editItem');

          for (const item of hiddenElements) {
            assert.equal(getComputedStyle(item).display, 'none');
          }

          MockInteractions.tap(editBtn);
          flushAsynchronousOperations();
          // The revision and edit button are not visible.
          assert.equal(getComputedStyle(revisionWithEditing).display, 'none');
          assert.equal(getComputedStyle(editBtn).display, 'none');

          // The input, cancel, and save buttons are not visible.
          for (item of hiddenElements) {
            assert.notEqual(getComputedStyle(item).display, 'none');
          }

          // The revised ref was set correctly
          assert.equal(element._revisedRef, 'master');

          assert.isFalse(saveBtn.disabled);

          // Delete the ref.
          element._revisedRef = '';
          assert.isTrue(saveBtn.disabled);

          // Change the ref to something else
          element._revisedRef = 'newRef';
          element._project = 'test';
          assert.isFalse(saveBtn.disabled);

          // Save button calls handleSave. since this is stubbed, the edit
          // section remains open.
          MockInteractions.tap(saveBtn);
          assert.isTrue(element._handleSaveRevision.called);

          // When cancel is tapped, the edit secion closes.
          MockInteractions.tap(cancelBtn);
          flushAsynchronousOperations();

          // The revision and edit button are visible.
          assert.notEqual(getComputedStyle(revisionWithEditing).display,
              'none');
          assert.notEqual(getComputedStyle(editBtn).display, 'none');

          // The input, cancel, and save buttons are not visible.
          for (const item of hiddenElements) {
            assert.equal(getComputedStyle(item).display, 'none');
          }
          done();
        });
      });

      test('_handleSaveRevision with invalid rev', done => {
        const event = {model: {set: sandbox.stub()}};
        element._isEditing = true;
        sandbox.stub(element.$.restAPI, 'setProjectHead').returns(
            Promise.resolve({
              status: 400,
            })
        );

        element._setProjectHead('test', 'newRef', event).then(() => {
          assert.isTrue(element._isEditing);
          assert.isFalse(event.model.set.called);
          done();
        });
      });

      test('_handleSaveRevision with valid rev', done => {
        const event = {model: {set: sandbox.stub()}};
        element._isEditing = true;
        sandbox.stub(element.$.restAPI, 'setProjectHead').returns(
            Promise.resolve({
              status: 200,
            })
        );

        element._setProjectHead('test', 'newRef', event).then(() => {
          assert.isFalse(element._isEditing);
          assert.isTrue(event.model.set.called);
          done();
        });
      });

      test('test _computeItemName', () => {
        assert.deepEqual(element._computeItemName('branches'), 'Branch');
        assert.deepEqual(element._computeItemName('tags'), 'Tag');
      });
    });

    suite('list with less then 25 branches', () => {
      setup(done => {
        branches = _.times(25, branchGenerator);

        stub('gr-rest-api-interface', {
          getProjectBranches(num, project, offset) {
            return Promise.resolve(branches);
          },
        });

        const params = {
          project: 'test',
          detailType: 'branches',
        };

        element._paramsChanged(params).then(() => { flush(done); });
      });

      test('_shownProjectsBranches', () => {
        assert.equal(element._shownItems.length, 25);
      });
    });

    suite('filter', () => {
      test('_paramsChanged', done => {
        sandbox.stub(element.$.restAPI, 'getProjectBranches', () => {
          return Promise.resolve(branches);
        });
        const params = {
          detailType: 'branches',
          project: 'test',
          filter: 'test',
          offset: 25,
        };
        element._paramsChanged(params).then(() => {
          assert.isTrue(element.$.restAPI.getProjectBranches.lastCall
              .calledWithExactly('test', 'test', 25, 25));
          done();
        });
      });
    });
  });

  suite('Tags', () => {
    let element;
    let tags;
    let sandbox;

    setup(() => {
      sandbox = sinon.sandbox.create();
      element = fixture('basic');
      element.detailType = 'tags';
      counter = 0;
      sandbox.stub(page, 'show');
    });

    teardown(() => {
      sandbox.restore();
    });

    suite('list of project tags', () => {
      setup(done => {
        tags = _.times(26, tagGenerator);

        stub('gr-rest-api-interface', {
          getProjectTags(num, project, offset) {
            return Promise.resolve(tags);
          },
        });

        const params = {
          project: 'test',
          detailType: 'tags',
        };

        element._paramsChanged(params).then(() => { flush(done); });
      });

      test('test for tag in the list', done => {
        flush(() => {
          assert.equal(element._items[1].ref, 'refs/tags/test2');
          done();
        });
      });

      test('test for tag message in the list', done => {
        flush(() => {
          assert.equal(element._items[1].message, 'Annotated tag');
          done();
        });
      });

      test('test for tagger in the tag list', done => {
        const tagger = {
          name: 'Test User',
          email: 'test.user@gmail.com',
          date: '2017-09-19 14:54:00.000000000',
          tz: 540,
        };
        flush(() => {
          assert.deepEqual(element._items[1].tagger, tagger);
          done();
        });
      });

      test('test for web links in the tags list', done => {
        flush(() => {
          assert.equal(element._items[1].web_links[0].url,
              'https://git.example.org/tag/test;refs/tags/test2');
          done();
        });
      });

      test('test for refs/tags/ being striped from ref', done => {
        flush(() => {
          assert.equal(element._stripRefs(element._items[1].ref,
              element.detailType), 'test2');
          done();
        });
      });

      test('_shownItems', () => {
        assert.equal(element._shownItems.length, 25);
      });

      test('_computeHideTagger', () => {
        const testObject1 = {
          tagger: 'test',
        };
        assert.equal(element._computeHideTagger(testObject1), '');

        assert.equal(element._computeHideTagger(undefined), 'hide');
      });
    });

    suite('list with less then 25 tags', () => {
      setup(done => {
        tags = _.times(25, tagGenerator);

        stub('gr-rest-api-interface', {
          getProjectTags(num, project, offset) {
            return Promise.resolve(tags);
          },
        });

        const params = {
          project: 'test',
          detailType: 'tags',
        };

        element._paramsChanged(params).then(() => { flush(done); });
      });

      test('_shownItems', () => {
        assert.equal(element._shownItems.length, 25);
      });
    });

    suite('filter', () => {
      test('_paramsChanged', done => {
        sandbox.stub(element.$.restAPI, 'getProjectTags', () => {
          return Promise.resolve(tags);
        });
        const params = {
          project: 'test',
          detailType: 'tags',
          filter: 'test',
          offset: 25,
        };
        element._paramsChanged(params).then(() => {
          assert.isTrue(element.$.restAPI.getProjectTags.lastCall
              .calledWithExactly('test', 'test', 25, 25));
          done();
        });
      });
    });

    suite('create new', () => {
      test('_handleCreateClicked called when create-click fired', () => {
        sandbox.stub(element, '_handleCreateClicked');
        element.$$('gr-list-view').fire('create-clicked');
        assert.isTrue(element._handleCreateClicked.called);
      });

      test('_handleCreateClicked opens modal', () => {
        const openStub = sandbox.stub(element.$.createOverlay, 'open');
        element._handleCreateClicked();
        assert.isTrue(openStub.called);
      });

      test('_handleCreateItem called when confirm fired', () => {
        sandbox.stub(element, '_handleCreateItem');
        element.$.createDialog.fire('confirm');
        assert.isTrue(element._handleCreateItem.called);
      });

      test('_handleCloseCreate called when cancel fired', () => {
        sandbox.stub(element, '_handleCloseCreate');
        element.$.createDialog.fire('cancel');
        assert.isTrue(element._handleCloseCreate.called);
      });
    });
  });
</script>
